/*
 * Copyright (C) 2021 XRADIO TECHNOLOGY CO., LTD. All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the
 *       distribution.
 *    3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of
 *       its contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "wifi_hotspot.h"
#include <securec.h>
#include "xr_wifi_adapter.h"
#include "wifi_device_util.h"

#define RSSI_LEVEL_4_2_G (-65)
#define RSSI_LEVEL_3_2_G (-75)
#define RSSI_LEVEL_2_2_G (-82)
#define RSSI_LEVEL_1_2_G (-88)

#define RSSI_LEVEL_4_5_G (-65)
#define RSSI_LEVEL_3_5_G (-72)
#define RSSI_LEVEL_2_5_G (-79)
#define RSSI_LEVEL_1_5_G (-85)

#define WIFI_TPC_MAX_POWER (20)
#define WIFI_TPC_MIN_POWER (-1) /* 0xFFFFFFFF */
#define WIFI_TPC_ID 35
#define WIFI_TPC_LEN 2

static int g_wifiApStatus = WIFI_HOTSPOT_NOT_ACTIVE;
static HotspotConfig g_wifiApConfig = { 0 };
static char g_wifiIfName[WIFI_IFNAME_MAX_SIZE + 1] = { 0 };
typedef struct {
	unsigned char id;
	unsigned char len;
	signed char power;
	unsigned char margin;
} TpcElement;

WifiErrorCode EnableHotspot(void)
{
	if (LockWifiGlobalLock() != WIFI_SUCCESS) {
		return ERROR_WIFI_UNKNOWN;
	}
	if (g_wifiApStatus == WIFI_HOTSPOT_ACTIVE) {
		if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
			return ERROR_WIFI_UNKNOWN;
		}
		return ERROR_WIFI_BUSY;
	}

	xr_wifi_softap_config hapdConf = { 0 };

	hapdConf.channel_num = HOTSPOT_DEFAULT_CHANNEL;
	if (g_wifiApConfig.channelNum) {
		hapdConf.channel_num = g_wifiApConfig.channelNum;
	}
	hapdConf.authmode = HoSecToHiSec(g_wifiApConfig.securityType);

	int cpyErr = memcpy_s(hapdConf.ssid, WIFI_MAX_SSID_LEN,
			      g_wifiApConfig.ssid, XR_WIFI_MAX_SSID_LEN + 1);
	cpyErr +=
		memcpy_s(hapdConf.key, WIFI_MAX_KEY_LEN,
			 g_wifiApConfig.preSharedKey, XR_WIFI_MAX_KEY_LEN + 1);
	if (cpyErr != EOK) {
		printf("[wifi_service]:EnableHotspot memcpy fail, err = %d\n",
		       cpyErr);
		if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
			return ERROR_WIFI_UNKNOWN;
		}
		return ERROR_WIFI_UNKNOWN;
	}

	int len = sizeof(g_wifiIfName);
	int hiRet = xr_wifi_softap_start(&hapdConf, g_wifiIfName, &len);
	if (hiRet != XR_WIFI_OK) {
		printf("[wifi_service]:EnableHotspot softap start fail, err = %d\n",
		       hiRet);
		if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
			return ERROR_WIFI_UNKNOWN;
		}
		return ERROR_WIFI_NOT_STARTED;
	}

	cpyErr = memset_s(&hapdConf, sizeof(xr_wifi_softap_config), 0,
			  sizeof(xr_wifi_softap_config));
	if (cpyErr != EOK) {
		printf("[wifi_service]:EnableHotspot memset fail, err = %d\n",
		       cpyErr);
		if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
			return ERROR_WIFI_UNKNOWN;
		}
		return ERROR_WIFI_UNKNOWN;
	}

	g_wifiApStatus = WIFI_HOTSPOT_ACTIVE;
	if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
		return ERROR_WIFI_UNKNOWN;
	}

	return WIFI_SUCCESS;
}

WifiErrorCode DisableHotspot(void)
{
	if (LockWifiGlobalLock() != WIFI_SUCCESS) {
		return ERROR_WIFI_UNKNOWN;
	}
	if (g_wifiApStatus == WIFI_HOTSPOT_NOT_ACTIVE) {
		if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
			return ERROR_WIFI_UNKNOWN;
		}
		return ERROR_WIFI_NOT_STARTED;
	}

	int hiRet = xr_wifi_softap_stop();
	if (hiRet != XR_WIFI_OK) {
		printf("[wifi_service]:DisableHotspot failed to stop softap, err = %d\n",
		       hiRet);
		if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
			return ERROR_WIFI_UNKNOWN;
		}
		return ERROR_WIFI_NOT_STARTED;
	}

	g_wifiApStatus = WIFI_HOTSPOT_NOT_ACTIVE;
	if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
		return ERROR_WIFI_UNKNOWN;
	}
	return WIFI_SUCCESS;
}

WifiErrorCode SetHotspotConfig(const HotspotConfig *config)
{
	if (config == NULL) {
		return ERROR_WIFI_INVALID_ARGS;
	}

	if (LockWifiGlobalLock() != WIFI_SUCCESS) {
		return ERROR_WIFI_UNKNOWN;
	}
	int cpyErr = memcpy_s(&g_wifiApConfig, sizeof(HotspotConfig), config,
			      sizeof(HotspotConfig));
	if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
		return ERROR_WIFI_UNKNOWN;
	}
	if (cpyErr != EOK) {
		printf("[wifi_service]:SetHotspotConfig memcpy fail, err = %d\n",
		       cpyErr);
		return ERROR_WIFI_UNKNOWN;
	}
	return WIFI_SUCCESS;
}

WifiErrorCode GetHotspotConfig(HotspotConfig *result)
{
	if (result == NULL) {
		return ERROR_WIFI_INVALID_ARGS;
	}

	if (LockWifiGlobalLock() != WIFI_SUCCESS) {
		return ERROR_WIFI_UNKNOWN;
	}
	int cpyErr = memcpy_s(result, sizeof(HotspotConfig), &g_wifiApConfig,
			      sizeof(HotspotConfig));
	if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
		return ERROR_WIFI_UNKNOWN;
	}
	if (cpyErr != EOK) {
		printf("[wifi_service]:SetHotspotConfig memcpy fail, err = %d\n",
		       cpyErr);
		return ERROR_WIFI_UNKNOWN;
	}
	return WIFI_SUCCESS;
}

int IsHotspotActive(void)
{
	if (LockWifiGlobalLock() != WIFI_SUCCESS) {
		return ERROR_WIFI_UNKNOWN;
	}
	int ret = g_wifiApStatus;
	if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
		return ERROR_WIFI_UNKNOWN;
	}

	return ret;
}

WifiErrorCode GetStationList(StationInfo *result, unsigned int *size)
{
	if (result == NULL || size == NULL || *size == 0) {
		return ERROR_WIFI_INVALID_ARGS;
	}

	xr_wifi_ap_sta_info staList[WIFI_MAX_STA_NUM] = { 0 };
	unsigned int staNum = 0;

	int hiRet = xr_wifi_softap_get_connected_sta(staList, &staNum);
	if (hiRet != XR_WIFI_OK) {
		printf("[wifi_service]:GetStationList get connected sta failed, err = %d\n",
		       hiRet);
		return ERROR_WIFI_NOT_AVAILABLE;
	}

	if (*size < staNum) {
		return ERROR_WIFI_INVALID_ARGS;
	}

	int cpyErr;
	for (unsigned int i = 0; i < staNum; i++) {
		cpyErr = memcpy_s(result[i].macAddress, WIFI_MAC_LEN,
				  staList[i].mac, WIFI_MAC_LEN);
		if (cpyErr != EOK) {
			printf("[wifi_service]:GetStationList memcpy fail, err = %d\n",
			       cpyErr);
			return ERROR_WIFI_UNKNOWN;
		}
	}

	*size = staNum;
	return WIFI_SUCCESS;
}

WifiErrorCode SetBand(int band)
{
	if (band != HOTSPOT_BAND_TYPE_2G) {
		return ERROR_WIFI_NOT_SUPPORTED;
	}
	if (LockWifiGlobalLock() != WIFI_SUCCESS) {
		return ERROR_WIFI_UNKNOWN;
	}
	g_wifiApConfig.band = band;
	if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
		return ERROR_WIFI_UNKNOWN;
	}
	return WIFI_SUCCESS;
}

WifiErrorCode GetBand(int *result)
{
	if (result == NULL) {
		return ERROR_WIFI_INVALID_ARGS;
	}

	if (LockWifiGlobalLock() != WIFI_SUCCESS) {
		return ERROR_WIFI_UNKNOWN;
	}
	if (g_wifiApConfig.band == 0) {
		if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
			return ERROR_WIFI_UNKNOWN;
		}
		return ERROR_WIFI_NOT_AVAILABLE;
	}
	if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
		return ERROR_WIFI_UNKNOWN;
	}
	*result = HOTSPOT_BAND_TYPE_2G;
	return WIFI_SUCCESS;
}

int GetSignalLevel(int rssi, int band)
{
	if (band == HOTSPOT_BAND_TYPE_2G) {
		if (rssi >= RSSI_LEVEL_4_2_G) {
			return RSSI_LEVEL_4;
		}
		if (rssi >= RSSI_LEVEL_3_2_G) {
			return RSSI_LEVEL_3;
		}
		if (rssi >= RSSI_LEVEL_2_2_G) {
			return RSSI_LEVEL_2;
		}
		if (rssi >= RSSI_LEVEL_1_2_G) {
			return RSSI_LEVEL_1;
		}
	}

	if (band == HOTSPOT_BAND_TYPE_5G) {
		if (rssi >= RSSI_LEVEL_4_5_G) {
			return RSSI_LEVEL_4;
		}
		if (rssi >= RSSI_LEVEL_3_5_G) {
			return RSSI_LEVEL_3;
		}
		if (rssi >= RSSI_LEVEL_2_5_G) {
			return RSSI_LEVEL_2;
		}
		if (rssi >= RSSI_LEVEL_1_5_G) {
			return RSSI_LEVEL_1;
		}
	}

	return ERROR_WIFI_INVALID_ARGS;
}

int GetHotspotChannel(void)
{
	if (LockWifiGlobalLock() != WIFI_SUCCESS) {
		return ERROR_WIFI_UNKNOWN;
	}
	if (g_wifiApStatus == WIFI_HOTSPOT_NOT_ACTIVE) {
		if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
			return ERROR_WIFI_UNKNOWN;
		}
		return ERROR_WIFI_NOT_STARTED;
	}

	int channel =
		xr_wifi_get_channel(g_wifiIfName, WIFI_IFNAME_MAX_SIZE + 1);
	if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
		return ERROR_WIFI_UNKNOWN;
	}
	if (channel == XR_WIFI_INVALID_CHANNEL) {
		return ERROR_WIFI_INVALID_ARGS;
	}

	return channel;
}

WifiErrorCode GetHotspotInterfaceName(char *result, int size)
{
	if (LockWifiGlobalLock() != WIFI_SUCCESS) {
		return ERROR_WIFI_UNKNOWN;
	}
	if (g_wifiApStatus == WIFI_HOTSPOT_NOT_ACTIVE) {
		if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
			return ERROR_WIFI_UNKNOWN;
		}
		return ERROR_WIFI_NOT_STARTED;
	}

	int cpyErr =
		memcpy_s(result, size, g_wifiIfName, WIFI_IFNAME_MAX_SIZE + 1);
	if (UnlockWifiGlobalLock() != WIFI_SUCCESS) {
		return ERROR_WIFI_UNKNOWN;
	}
	if (cpyErr != EOK) {
		printf("[wifi_service]:getifname memcpy fail, err = %d\n",
		       cpyErr);
		return ERROR_WIFI_UNKNOWN;
	}
	return WIFI_SUCCESS;
}

WifiErrorCode DisassociateSta(unsigned char *mac, int macLen)
{
	/* TODO: not supported by the current version */
	return ERROR_WIFI_NOT_SUPPORTED;
}

WifiErrorCode AddTxPowerInfo(int power)
{
	int ret;

	TpcElement tpc = { WIFI_TPC_ID, WIFI_TPC_LEN, 0, 0 };
	if (power > WIFI_TPC_MAX_POWER) {
		printf("[wifi_service]:Invalid TPC Power (%d)\n", power);
		return ERROR_WIFI_INVALID_ARGS;
	} else if (power <= WIFI_TPC_MIN_POWER) {
		xr_wifi_set_appie(IEEE80211_FC_STYPE_BEACON, NULL, 0);
		xr_wifi_set_appie(IEEE80211_FC_STYPE_PROBE_RESP, NULL, 0);
		return WIFI_SUCCESS;
	} else {
		tpc.power = power;
	}

	ret = xr_wifi_set_appie(IEEE80211_FC_STYPE_BEACON, (uint8_t *)&tpc,
				sizeof(tpc));
	if (ret != XR_WIFI_OK) {
		printf("[wifi_service]:Wifi Add Beacon IE Fail (%d)\n", ret);
		xr_wifi_set_appie(IEEE80211_FC_STYPE_BEACON, NULL, 0);
		return ERROR_WIFI_UNKNOWN;
	}

	ret = xr_wifi_set_appie(IEEE80211_FC_STYPE_PROBE_RESP, (uint8_t *)&tpc,
				sizeof(tpc));
	if (ret != XR_WIFI_OK) {
		printf("[wifi_service]:Wifi Add ProbResp IE Fail (%d)\n", ret);
		xr_wifi_set_appie(IEEE80211_FC_STYPE_PROBE_RESP, NULL, 0);
		return ERROR_WIFI_UNKNOWN;
	}

	return WIFI_SUCCESS;
}
